package the.guanzhong.express.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.wisteria.framework.util.DateUtils;
import org.wisteria.framework.util.MoneyUtil;
import org.wisteria.framework.web.BusinessException;
import org.wisteria.framework.web.QueryResult;
import the.broccoli.uc.entity.User;
import the.broccoli.uc.service.IUserService;
import the.broccoli.weixin.service.WeiXinPayService;
import the.broccoli.weixin.service.dto.WeiXinUnifiedOrderDTO;
import the.broccoli.weixin.service.dto.WeiXinUnifiedOrderResultDTO;
import the.guanzhong.delivery.interfaces.controller.resp.TaskItemResp;
import the.guanzhong.delivery.service.DeliveryTaskService;
import the.guanzhong.express.infrastructure.common.enums.CargoTypeEnum;
import the.guanzhong.express.infrastructure.common.enums.OrderStatusEnum;
import the.guanzhong.express.infrastructure.common.enums.PayProviderEnum;
import the.guanzhong.express.infrastructure.mapper.ExpressOrderMapper;
import the.guanzhong.express.infrastructure.mapper.condition.OrderPageCondition;
import the.guanzhong.express.infrastructure.model.GoodsInfoDO;
import the.guanzhong.express.infrastructure.model.OrderDO;
import the.guanzhong.express.interfaces.controller.req.*;
import the.guanzhong.express.interfaces.controller.resp.OrderAddResp;
import the.guanzhong.express.interfaces.controller.resp.OrderListResp;
import the.guanzhong.express.interfaces.controller.resp.UserOrderTodayResp;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 订单信息服务类
 */
@Slf4j
@Service
public class ExpressOrderService {

    @Resource
    private ExpressOrderMapper expressOrderMapper;

    @Resource
    private WeiXinPayService weiXinPayService;

    @Resource
    private DeliveryTaskService deliveryTaskService;

    @Resource
    private IUserService userService;

    @Resource
    private ExpressRefundService expressRefundService;

    /**
     * 保存订单
     * @param req   订单信息
     * @return
     */
    public OrderAddResp save(OrderAddReq req) {
        OrderDO order = new OrderDO();
        order.setCreatedTime(new Date());
        order.setModifiedTime(new Date());
        order.setTenantId(req.getTenantId());
        order.setUserId(req.getUserId());
        order.setOrderTime(new Date());
        order.setOrderStatus(OrderStatusEnum.PRE_PAY.getCode());

        order.setFromTakeType(req.getFromTakeType());
        order.setFromShopId(req.getFromShopId());
        order.setFromShopName(req.getFromShopName());
        order.setFromLinkman(req.getFromLinkman());
        order.setFromPhone(req.getFromPhone());
        order.setFromAddress(req.getFromAddress());

        order.setToTakeType(req.getToTakeType());
        order.setToShopId(req.getToShopId());
        order.setToShopName(req.getToShopName());
        order.setToLinkman(req.getToLinkman());
        order.setToPhone(req.getToPhone());
        order.setToAddress(req.getToAddress());

        order.setTransWay(req.getTransWay());
        order.setAgreementFee(req.getAgreementFee());
        order.setCouponId(req.getCouponId());
        if (!CollectionUtils.isEmpty(req.getGoodsList())) {
            order.setGoodsInfo(JSON.toJSONString(req.getGoodsList()));
        }

//        order.setPayTime();
        order.setPayFee(req.getPayFee());
        order.setPayProvider(PayProviderEnum.WEI_PAY.getCode());

        order.setIfOften(req.getIfOften());
        expressOrderMapper.insert(order);

        WeiXinUnifiedOrderDTO unifiedOrder = new WeiXinUnifiedOrderDTO();
        unifiedOrder.setBody("海陆空快运-货运单支付");
        unifiedOrder.setOutTradeNo(String.valueOf(order.getId()));
        unifiedOrder.setTotalFee(MoneyUtil.yuan2Fen(order.getPayFee()));
        unifiedOrder.setSpbillCreateIp("172.0.0.1");
        unifiedOrder.setNotifyUrl("https://hello5688.com/req/express/order/pay");
        unifiedOrder.setOpenId(req.getOpenId());
        WeiXinUnifiedOrderResultDTO result = null;
        try {
            result = weiXinPayService.unifiedOrder(unifiedOrder);
        } catch (WxPayException e) {
            log.error("微信支付，创建预订单异常。", e);
            throw BusinessException.withMessage("微信支付，创建预订单异常");
        }

        if (result == null) {
            return null;
        }

        OrderAddResp resp = new OrderAddResp();
        resp.setPrepayId(result.getPrepayId());
        resp.setNonceStr(result.getNonceStr());
        resp.setPaySign(result.getPaySign());
        resp.setSignType(result.getSignType());
        resp.setTimeStamp(result.getTimeStamp());
        resp.setOrderId(order.getId());
        return resp;
    }

    /**
     * 修改订单状态
     * @param req   订单信息
     * @return
     */
    public boolean modifyOrderStatus(OrderStatusModifyReq req) {
        return expressOrderMapper.update(null, new LambdaUpdateWrapper<OrderDO>()
                .set(OrderDO::getModifiedTime, new Date())
                .set(OrderDO::getOrderStatus, req.getOrderStatus())

                .eq(OrderDO::getId, req.getOrderId())
        ) > 0;
    }

    /**
     * 查询订单列表
     * @param req   查询条件
     * @return
     */
    public List<OrderListResp> list(OrderListReq req) {
        LambdaQueryWrapper<OrderDO> queryWrapper = new LambdaQueryWrapper<OrderDO>()
                .eq(OrderDO::getUserId, req.getUserId());
        if (!CollectionUtils.isEmpty(req.getOrderStatusList())) {
            queryWrapper.in(OrderDO::getOrderStatus, req.getOrderStatusList());
        } else if (req.getOrderStatus() != null) {
            queryWrapper.eq(OrderDO::getOrderStatus, req.getOrderStatus());
        } else {
            queryWrapper.in(OrderDO::getOrderStatus, OrderStatusEnum.WAITING_PAY.getCode(),
                                                    OrderStatusEnum.WAITING_RECEIVE.getCode(),
                                                    OrderStatusEnum.RECEIVED.getCode(),
                                                    OrderStatusEnum.TOOK.getCode(),
                                                    OrderStatusEnum.HAULAGE.getCode(),
                                                    OrderStatusEnum.COMPLETED.getCode());
        }
        queryWrapper.eq(req.getIfOften() != null, OrderDO::getIfOften, req.getIfOften())
                    .orderByDesc(OrderDO::getOrderTime);
        List<OrderDO> orderList = expressOrderMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(orderList)) {
            return null;
        }

        List<OrderListResp> dataList = orderList.stream().map(this::convertOf).collect(Collectors.toList());



        return dataList;
    }

    /**
     * 取消订单
     * @param req   订单信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean cancel(OrderCancelReq req) {
        int res = expressOrderMapper.update(null, new LambdaUpdateWrapper<OrderDO>()
                .set(OrderDO::getOrderStatus, OrderStatusEnum.REFUND.getCode())
                .set(OrderDO::getModifiedTime, new Date())

                .eq(OrderDO::getId, req.getOrderId())
        );
        if (res <= 0) {
            return false;
        }

        RefundApplyReq refundApply = new RefundApplyReq();
        refundApply.setOrderId(req.getOrderId());
        refundApply.setReason(req.getReason());
        return expressRefundService.apply(refundApply);
    }

    private final ReentrantLock payLock = new ReentrantLock();

    /**
     * 订单完成支付
     * @param req   支付信息
     * @return
     */
    public boolean completePayment(OrderCompletePaymentReq req) {
        boolean lock = payLock.tryLock();
        if (lock) {
            try {

                if (StringUtils.hasText(req.getOutTransactionId())) {
                    expressOrderMapper.update(null, new LambdaUpdateWrapper<OrderDO>()
                            .set(OrderDO::getOutTransactionId, req.getOutTransactionId())
                            .set(OrderDO::getModifiedTime, new Date())

                            .eq(OrderDO::getId, req.getOrderId())
                            .isNull(OrderDO::getOutTransactionId)
                    );
                }

                int res = expressOrderMapper.update(null, new LambdaUpdateWrapper<OrderDO>()
                        .set(OrderDO::getModifiedTime, new Date())
                        .set(OrderDO::getOrderStatus, OrderStatusEnum.WAITING_RECEIVE.getCode())
                        .set(OrderDO::getPayTime, new Date())
                        .set(OrderDO::getPayProvider, PayProviderEnum.WEI_PAY.getCode())
                        .set(OrderDO::getModifiedTime, new Date())

                        .eq(OrderDO::getId, req.getOrderId())
                        .in(OrderDO::getOrderStatus, OrderStatusEnum.WAITING_PAY.getCode(), OrderStatusEnum.PRE_PAY.getCode())
                );

                if (res > 0) {
                    try {
                        deliveryTaskService.accept(req.getOrderId());
                    } catch (Exception e) {
                        log.error("创建配送任务异常. OrderId: {}", req.getOrderId(), e);
                    }
                    return true;
                }
            } catch (Exception e) {
                log.error("更新订单完成支付异常. OrderId: {}", req.getOrderId(), e);
            } finally {
                if (payLock.isLocked() && payLock.isHeldByCurrentThread()) {
                    payLock.unlock();
                }
            }
        }

        return false;
    }

    /**
     * 获取订单信息
     * @param orderId   订单ID
     * @return
     */
    public OrderListResp get(Integer orderId) {
        OrderDO entity = expressOrderMapper.selectById(orderId);
        return entity == null ? null : convertOf(entity);
    }

    /**
     * 订单分页查询
     * @param req   查询条件
     * @return
     */
    public QueryResult<OrderListResp> page(OrderPageReq req) {
        if (req.getPageNum() != null && req.getPageSize() != null) {
            PageHelper.startPage(req.getPageNum(), req.getPageSize());
        }

        List<OrderListResp> dataList = new ArrayList<>();

        OrderPageCondition condition = new OrderPageCondition();
        condition.setPageNum(req.getPageNum());
        condition.setPageSize(req.getPageSize());
        condition.setOrderStatus(req.getOrderStatus());

        Page<OrderDO> page = this.expressOrderMapper.selectListPage(condition);
        if (!CollectionUtils.isEmpty(page.getResult())) {
            dataList = page.getResult().stream().map(this::convertOf).collect(Collectors.toList());

            List<Integer> orderIdList = dataList.stream().map(OrderListResp::getOrderId).collect(Collectors.toList());
            List<TaskItemResp> taskList = deliveryTaskService.selectBatchOrderIds(orderIdList);
            if (!CollectionUtils.isEmpty(taskList)) {
                List<String> receivingUserIdList = taskList.stream().map(TaskItemResp::getReceivingUserId).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(receivingUserIdList)) {
                    List<User> userList = (List<User>) userService.listByIds(new HashSet<>(receivingUserIdList));
                    if (!CollectionUtils.isEmpty(userList)) {
                        Map<String, String> userMap = userList.stream().collect(Collectors.toMap(User::getUserId, User::getUserName, (a, b) -> b));
                        Map<Integer, TaskItemResp> taskReceiveMap = taskList.stream().collect(Collectors.toMap(TaskItemResp::getOrderId, it -> it, (a, b) -> b));
                        dataList.forEach(it -> {
                            TaskItemResp taskItem = taskReceiveMap.get(it.getOrderId());
                            if (taskItem != null) {
                                it.setReceivingTime(DateUtils.formatTime(taskItem.getReceivingTime()));
                                it.setReceivingUserName(userMap.get(taskItem.getReceivingUserId()));
                            }
                        });
                    }
                }
            }
        }

        return new QueryResult<OrderListResp>(page.getTotal(), dataList);
    }

    private OrderListResp convertOf(OrderDO source) {
        OrderListResp target = new OrderListResp();
        target.setOrderId(source.getId());
        target.setOrderTime(DateUtils.formatTime(source.getOrderTime()));
        target.setFromShopId(source.getFromShopId());
        target.setFromShopName(source.getFromShopName());
        target.setFromAddress(source.getFromAddress());
        target.setFromLinkman(source.getFromLinkman());
        target.setFromPhone(source.getFromPhone());
        target.setToAddress(source.getToAddress());
        target.setToLinkman(source.getToLinkman());
        target.setToPhone(source.getToPhone());
        target.setToShopId(source.getToShopId());
        target.setToShopName(source.getToShopName());
        target.setOrderStatus(source.getOrderStatus());
        target.setPayFee(source.getPayFee());
        target.setTransWay(source.getTransWay());
        target.setFromTakeType(source.getFromTakeType());
        target.setToTakeType(source.getToTakeType());
        target.setStatusName(OrderStatusEnum.getNameByCode(source.getOrderStatus()));
        if (StringUtils.hasText(source.getGoodsInfo())) {
            target.setGoodsList(JSON.parseArray(source.getGoodsInfo(), GoodsInfoDO.class));
        }
        return target;
    }
}
